using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using DirectShowLib;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace Lizk.DShowPlayer
{
    
    public abstract class Player : IDisposable
    {
        [Flags] public enum ProcAmp { Brightness = 1, Contrast = 2, Saturation = 4, Hue = 8, None = 0 } 
        protected const int WMGraphNotify = 0x0400 + 13;
        protected bool noVideo = false;
        protected EventHandler resizeEvent;
        protected PlayerState state = PlayerState.NotLoaded;
        public static uint INFINITE = 0xFFFFFFFF;
        protected Control targetControl;
        protected Form mainForm;

        private ProcAmp procAmpCaps;
        public ProcAmp ProcAmpCaps{get { return procAmpCaps; } }

        protected Dictionary<int, string> audioTracks = new Dictionary<int, string>();
        protected Dictionary<int, string> subtitles = new Dictionary<int, string>();
        public Dictionary<int, string> Subtitles { get { return subtitles; } }
        public Dictionary<int, string> AudioTracks { get { return audioTracks; } }

        public abstract void Load(Control targetControl);
        public abstract void Load(Form owner, IVMRSurfaceAllocator9 allocator);

        protected int subtitle;
        public int Subtitle
        {
            get { return subtitle; }
            set
            {
                if (value == subtitle)
                    return;
                if (subtitles.ContainsKey(value))
                {
                    EnableSubtitle(value);
                    if (LanguageChanged != null)
                    {
                        LanguageChanged(this, new LanguageEventArgs(this.subtitle, this.Subtitles.Count > 0 && subtitle >= 0 ? this.subtitles[subtitle] : null, this.audioTrack, this.AudioTracks.Count > 0 && audioTrack >= 0 ? this.audioTracks[this.audioTrack] : null));
                    }
                }
            }
        }
        protected int audioTrack;
        
        public int AudioTrack
        {
            get { return audioTrack; }
            set
            {
                if (value == audioTrack)
                    return;
                if (audioTracks.ContainsKey(value))
                {
                    EnableAudioTrack(value);
                    if (LanguageChanged != null)
                    {
                        LanguageChanged(this, new LanguageEventArgs(this.subtitle, this.Subtitles.Count > 0 && subtitle >= 0 ? this.subtitles[subtitle] : null, this.audioTrack, this.AudioTracks.Count > 0 && audioTrack >= 0 ? this.audioTracks[this.audioTrack] : null));
                    }
                }
            }
        }

        protected void UpdateTrackAmount()
        {
            if (TrackAmountChanged != null)
                TrackAmountChanged(this, EventArgs.Empty);
        }


        protected void ChangeState(FilterState fs)
        {
            PlayerState oldState = state;
            switch (fs)
            {
                case FilterState.Paused:
                    state = PlayerState.Paused;
                    break;
                case FilterState.Running:
                    state = PlayerState.Playing;
                    break;
                case FilterState.Stopped:
                    state = PlayerState.Loaded;
                    break;
                default:
                    break;
            }
            
            if (StateChanged != null)
            {
                StateChanged(this, new PlayerEventArgs(state, oldState));
            }
        }

        protected void ChangeState(PlayerState newState)
        {
            PlayerState oldState = state;
            state = newState;
            if (StateChanged != null)
            {
                StateChanged(this, new PlayerEventArgs(newState, oldState));
            }
        }

        public event EventHandler<PlayerEventArgs> StateChanged;
        public event EventHandler<LanguageEventArgs> LanguageChanged;
        public event EventHandler TrackAmountChanged;
        public event EventHandler SetProcAmp;

        protected TimeSpan duration = TimeSpan.Zero;
        public TimeSpan Position
        {
            get { return GetPosition(); }
        }
        public TimeSpan Duration
        {
            get { return duration; }
        }
        public PlayerState State
        {
            get { return state; }
        }

        public abstract void EnableSubtitle(int id);
        public abstract void EnableAudioTrack(int id);
        protected abstract TimeSpan GetPosition();
        #region DShowVariables
        protected IFilterGraph2 graphBuilder = null;     // Needs to be disposed
        protected IMediaControl mediaControl = null;     // Is Disposed with graphBuilder
        protected IMediaSeeking mediaSeeking = null;     // Is Disposed with graphBuilder
        protected IMediaPosition mediaPosition = null;   // Is Disposed with graphBuilder
        protected IMediaEventEx mediaEventEx = null;     // Is Disposed with graphBuilder
        protected IVideoWindow videoWindow = null;       // Is Disposed with graphBuilder, for rendering video in forms
        protected IBasicVideo basicVideo = null;         // Is Disposed with graphBuilder, for controlling video.
        protected IBasicAudio basicAudio = null;         // Is Disposed with graphBuilder, for controlling audio.
        protected IVideoFrameStep frameStep = null;      // Is Disposed with graphBuilder
        protected AMSeekingSeekingCapabilities seekCap; // Enum, no memory management
        protected IBaseFilter vmr9;                      // Needs to be Disposed
        protected IVMRMixerControl9 vmrMixerControl;    // is disposed with vmr9
#if DEBUG
        protected DsROTEntry remoteGraph = null;         // Needs to be disposed
#endif
        protected VMR9ProcAmpControl defaultProcAmp = new VMR9ProcAmpControl();

        #endregion


        public abstract void Play();


        public bool HasVideo { get { return !noVideo; } }

        protected abstract bool Message(ref Message m);

        public bool PreFilterMessage(ref Message m)
        {
            // Pass this message to the video window for notification of system changes
            if (videoWindow != null)
                videoWindow.NotifyOwnerMessage(m.HWnd, m.Msg, m.WParam, m.LParam);

            if (m.LParam.ToInt32() != GetHashCode())
                return false;


            return Message(ref m);
        }


        public IBaseFilter GrabVMR9Filter()
        {
            return vmr9;
        }

        public void AddVmr9(IVMRSurfaceAllocator9 allocator)
        {
            vmr9 = (IBaseFilter)new VideoMixingRenderer9();
            int hr;
            IVMRFilterConfig9 filterConfig = (IVMRFilterConfig9)vmr9;
            hr = filterConfig.SetRenderingMode(VMR9Mode.Renderless);
            DsError.ThrowExceptionForHR(hr);

            hr = filterConfig.SetNumberOfStreams(2);
            DsError.ThrowExceptionForHR(hr);

            IVMRSurfaceAllocatorNotify9 vmrSurfAllocNotify = (IVMRSurfaceAllocatorNotify9)vmr9;
            hr = vmrSurfAllocNotify.AdviseSurfaceAllocator(IntPtr.Zero, allocator);
            DsError.ThrowExceptionForHR(hr);

            hr = allocator.AdviseNotify(vmrSurfAllocNotify);
            DsError.ThrowExceptionForHR(hr);


            vmrMixerControl = (IVMRMixerControl9)vmr9;


            hr = vmrMixerControl.SetMixingPrefs(VMR9MixerPrefs.RenderTargetYUV | VMR9MixerPrefs.NoDecimation | VMR9MixerPrefs.ARAdjustXorY | VMR9MixerPrefs.AnisotropicFiltering);
            DsError.ThrowExceptionForHR(hr);


            hr = graphBuilder.AddFilter(vmr9, "Video Mixing Renderer 9");
            DsError.ThrowExceptionForHR(hr);


        }

        #region ProcAmp
        public bool NeedColorUpdate { get { return updateColors != ProcAmp.None; }  }

        private ProcAmp updateColors = ProcAmp.None;
        protected float brightness, hue, contrast, saturation;
        protected float maxBrightness, minBrightness, stepBrightness;
        protected float maxHue, minHue, stepHue;
        protected float maxContrast, minContrast, stepContrast;
        protected float maxSaturation, minSaturation, stepSaturation;

        public float MaxBrightness { get { return maxBrightness; } }
        public float MinBrightness { get { return minBrightness; } }
        public float StepBrightness { get { return stepBrightness; } }
        public float MaxContrast { get { return maxContrast; } }
        public float MinContrast { get { return minContrast; } }
        public float StepContrast { get { return stepContrast; } }
        public float MaxSaturation { get { return maxSaturation; } }
        public float MinSaturation { get { return minSaturation; } }
        public float StepSaturation { get { return stepSaturation; } }
        public float MaxHue { get { return maxHue; } }
        public float MinHue { get { return minHue; } }
        public float StepHue { get { return stepHue; } }



        public float Brightness { get { return brightness; } set { 
            if (brightness != value) 
                updateColors |= ProcAmp.Brightness; 
            brightness = value; } }
        public float Hue { get { return hue; } set { if (hue != value) updateColors |= ProcAmp.Hue; hue = value; } }
        public float Contrast { get { return contrast; } set { if (contrast != value) updateColors |= ProcAmp.Contrast; contrast = value; } }
        public float Saturation { get { return saturation; } set { if (saturation != value) updateColors |= ProcAmp.Saturation; saturation = value; } }

        public void AllocatorDoColorUpdate()
        {
            IVMRMixerControl9 vmrMixerControl = (IVMRMixerControl9)vmr9;
            VMR9ProcAmpControl procControl = new VMR9ProcAmpControl();
            procControl.dwSize = Marshal.SizeOf(typeof(VMR9ProcAmpControl));
            procControl.Brightness = brightness;
            procControl.Contrast = contrast;
            procControl.Hue = hue;
            procControl.Saturation = saturation;

            Debug.WriteLine("Set: " + brightness + " | " +contrast + " | " +saturation + " | " +hue);

            procControl.dwFlags = ConvertProcampFlags(updateColors);
            updateColors = ProcAmp.None;
            int hr = vmrMixerControl.SetProcAmpControl(0, ref procControl);
            DsError.ThrowExceptionForHR(hr);

            SetPictureAdjustments();
            if (SetProcAmp != null)
                SetProcAmp(this, EventArgs.Empty);
        }

        protected void SetPictureAdjustments()
        {
            VMR9ProcAmpControl procControl = new VMR9ProcAmpControl();
            procControl.dwSize = Marshal.SizeOf(typeof(VMR9ProcAmpControl));
            procControl.dwFlags = VMR9ProcAmpControlFlags.Brightness | VMR9ProcAmpControlFlags.Contrast | VMR9ProcAmpControlFlags.Hue | VMR9ProcAmpControlFlags.Saturation;

            int hr = vmrMixerControl.GetProcAmpControl(0, ref procControl);
            DsError.ThrowExceptionForHR(hr);

            procAmpCaps = ProcAmp.None;
            if ((procControl.dwFlags & VMR9ProcAmpControlFlags.Brightness) == VMR9ProcAmpControlFlags.Brightness)
            {
                brightness = procControl.Brightness;
                procAmpCaps |= ProcAmp.Brightness;
            }
            if ((procControl.dwFlags & VMR9ProcAmpControlFlags.Contrast) == VMR9ProcAmpControlFlags.Contrast)
            {
                contrast = procControl.Contrast;
                procAmpCaps |= ProcAmp.Contrast;
            }
            if ((procControl.dwFlags & VMR9ProcAmpControlFlags.Saturation) == VMR9ProcAmpControlFlags.Saturation)
            {
                saturation = procControl.Saturation;
                procAmpCaps |= ProcAmp.Saturation;
            }
            if ((procControl.dwFlags & VMR9ProcAmpControlFlags.Hue) == VMR9ProcAmpControlFlags.Hue)
            {
                hue = procControl.Hue;
                procAmpCaps |= ProcAmp.Hue;
            }
            Debug.WriteLine("Get: " + brightness + " | " + contrast + " | " + saturation + " | " + hue);
        }

        protected void GetProcAmp()
        {
            return; // Disable Procamp cuz it doesnt work... :(
           /* SetPictureAdjustments();
            VMR9ProcAmpControlRange procAmpRange = new VMR9ProcAmpControlRange();
            procAmpRange.dwSize = Marshal.SizeOf(typeof(VMR9ProcAmpControlRange));

            if ((procAmpCaps & ProcAmp.Brightness) == ProcAmp.Brightness)
            {
                procAmpRange.dwProperty = VMR9ProcAmpControlFlags.Brightness;
                DsError.ThrowExceptionForHR(vmrMixerControl.GetProcAmpControlRange(0, ref procAmpRange));
                maxBrightness = procAmpRange.MaxValue;
                minBrightness = procAmpRange.MinValue;
                stepBrightness = procAmpRange.StepSize;
            }
            if ((procAmpCaps & ProcAmp.Contrast) == ProcAmp.Contrast)
            {
                procAmpRange.dwProperty = VMR9ProcAmpControlFlags.Contrast;
                DsError.ThrowExceptionForHR(vmrMixerControl.GetProcAmpControlRange(0, ref procAmpRange));
                maxContrast = procAmpRange.MaxValue;
                minContrast = procAmpRange.MinValue;
                stepContrast = procAmpRange.StepSize;
            }
            if ((procAmpCaps & ProcAmp.Hue) == ProcAmp.Hue)
            {
                procAmpRange.dwProperty = VMR9ProcAmpControlFlags.Hue;
                DsError.ThrowExceptionForHR(vmrMixerControl.GetProcAmpControlRange(0, ref procAmpRange));
                maxHue = procAmpRange.MaxValue;
                minHue = procAmpRange.MinValue;
                stepHue = procAmpRange.StepSize;
            }
            if ((procAmpCaps & ProcAmp.Saturation) == ProcAmp.Saturation)
            {
                procAmpRange.dwProperty = VMR9ProcAmpControlFlags.Saturation;
                DsError.ThrowExceptionForHR(vmrMixerControl.GetProcAmpControlRange(0, ref procAmpRange));
                maxSaturation = procAmpRange.MaxValue;
                minSaturation = procAmpRange.MinValue;
                stepSaturation = procAmpRange.StepSize;
            }*/
        }

        private static VMR9ProcAmpControlFlags ConvertProcampFlags(ProcAmp flags)
        {
            VMR9ProcAmpControlFlags f = VMR9ProcAmpControlFlags.None;
            if ((flags & ProcAmp.Brightness) == ProcAmp.Brightness)
            {
                f |= VMR9ProcAmpControlFlags.Brightness;
            }
            if ((flags & ProcAmp.Contrast) == ProcAmp.Contrast)
            {
                f |= VMR9ProcAmpControlFlags.Contrast;
            }
            if ((flags & ProcAmp.Hue) == ProcAmp.Hue)
            {
                f |= VMR9ProcAmpControlFlags.Hue;
            }
            if ((flags & ProcAmp.Saturation) == ProcAmp.Saturation)
            {
                f |= VMR9ProcAmpControlFlags.Saturation;
            }
            return f;
        }


        #endregion

        public abstract void Dispose();
    }


}
